Function.prototype.myCall = function(ctx, ...args) {
    // 判断ctx类型，如果是基础类型，通过Object把它变成包装类型
    ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
    const fn = this
    // 不直接使用ctx.fn = fn,避免传入的对象中存在fn函数被覆盖，所以使用symbol保持唯一性
    const key = Symbol()
    // 不直接使用ctx[key] = fn,因为不想被遍历到
    Object.defineProperty(ctx, key, {
        value: fn,
        enumerable: false
    })
    const result = ctx[key](...args)
    // 执行完成后删除添加的fn
    delete ctx[key]
    return result
}

// args是个数组
Function.prototype.myApply = function(ctx, args) {
    // 判断ctx类型，如果是基础类型，通过Object把它变成包装类型
    ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
    const fn = this
    // 不直接使用ctx.fn = fn,避免传入的对象中存在fn函数被覆盖，所以使用symbol保持唯一性
    const key = Symbol()
    // 不直接使用ctx[key] = fn,因为不想被遍历到
    Object.defineProperty(ctx, key, {
        value: fn,
        enumerable: false
    })
    const result = ctx[key](...args)
    // 执行完成后删除添加的fn
    delete ctx[key]
    return result
}

Function.prototype.myBind = function(ctx, ...args) {
    // 判断ctx类型，如果是基础类型，通过Object把它变成包装类型
    ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
    const fn = this
    // 不直接使用ctx.fn = fn,避免传入的对象中存在fn函数被覆盖，所以使用symbol保持唯一性
    const key = Symbol()
    // 不直接使用ctx[key] = fn,因为不想被遍历到
    Object.defineProperty(ctx, key, {
        value: fn,
        enumerable: false
    })
    return function(...restArgs) {
        const params = [...args, ...restArgs]
        // 判断是否是new操作符
        if (new.target) {
            return new ctx[key](...params)
        } else {
            return ctx[key](...params)
        }
    }
}

Function.prototype.myBind2 = function(ctx, ...args) {
    const fn = this
    return function(...restArgs) {
        const params = [...args, ...restArgs]
        if (new.target) {
            return new fn.apply(ctx, params)
        } else {
            return fn.apply(ctx, params)
        }
    }
}